import type { AppConfig } from '@shared/types/config'

import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import process from 'node:process'
import { app } from 'electron'
import { providers } from './proxy'
// ===============================
// 配置管理模块
// ===============================
// 负责加载、管理和提供应用程序的全局配置
// 支持环境变量覆盖和自定义配置文件
// ===============================

// 全局配置对象，初始化时为null，通过loadConfig()加载
let config: AppConfig | null = null

// ===============================
// 默认配置定义
// ===============================
// 所有配置项的默认值，支持通过环境变量覆盖
// 生产环境建议通过配置文件或环境变量设置敏感信息
// ===============================

const defaultConfig: AppConfig = {
  // -----------------------------
  // 代理配置
  // -----------------------------
  // 用于网络请求的代理设置，支持HTTP/HTTPS/SOCKS5代理
  // 在网络受限或需要IP轮换的环境中使用
  // -----------------------------
  proxy: {
    enabled: true, // 是否启用代理功能
    // 多服务商定义：可扩展多个免费/付费来源（统一入口）
    providers,
    // 代理池规模控制（避免2w+动态池导致内存与序列化压力）
    max_active_pool_per_provider: 500,
    max_proxies_overall: 2000,
    save_full_pool: false,
    rotate_mode: 'per_session', // 代理轮换模式：per_session(每会话) | per_request(每请求)
    sticky_minutes: 1, // 代理粘性时间（分钟），per_session模式下的代理保持时间
    // 扩展：加入网络/隧道相关错误，触发自动轮换
    // 触发代理轮换的错误码/关键词（忽略大小写）
    // 说明：这里既包含 HTTP 状态码，也包含浏览器/Node 级网络错误与站点业务错误码
    rotate_on_errors: [
      'ip_lock_512', // 统一标签：检测到 512 阻挡页/IP 限制（内部约定）
      '429', // Too Many Requests（限流，建议切换代理并降低频率）
      '403', // Forbidden（IP/区域/ACL 限制，或代理出口被封）
      '407', // Proxy Authentication Required（代理认证失败/套餐失效/用量耗尽）
      'ERR_TUNNEL_CONNECTION_FAILED', // HTTPS CONNECT 隧道建立失败（常见于代理/出口网络异常）
      'ERR_PROXY_CONNECTION_FAILED', // 代理连接失败（代理服务器不可达/拒绝）
      'ERR_CONNECTION_CLOSED', // 连接被对端提前关闭
      'ERR_EMPTY_RESPONSE', // 目标无响应（浏览器层错误，见运行日志）
      'ERR_FAILED', // 浏览器通用错误（可能是网络/证书/策略等原因）
      'ECONNRESET', // 套接字被重置（RST），常见于中途断开
      'ETIMEDOUT', // 连接/请求超时
      'CONNECT TIMEOUT ERROR', // undici/自定义错误文案：连接超时
      'THIS OPERATION WAS ABORTED', // undici中止错误
      'CLIENT NETWORK SOCKET DISCONNECTED BEFORE SECURE TLS CONNECTION WAS ESTABLISHED', // Node原生HTTPS在TLS前断开
      'SOCKET HANG UP', // 套接字挂起
      'EPIPE', // 管道断裂
      'ERR_SSL_WRONG_VERSION_NUMBER', // SSL版本错误
      'ERR_SSL_PROTOCOL_ERROR', // SSL协议错误
      'ENOTFOUND', // DNS 解析失败（目标或代理主机名无法解析）
      '512-E-0044', // 站点业务阻挡码：重复刷新/短时间过多请求等
    ],
    max_concurrent_sessions: 50, // 最大并发会话数，避免过度使用代理资源
    max_concurrent_per_ip: 1, // 每个IP的最大并发租约数（用于ProxyLease机制，避免并发会话复用同一代理）
    // 关键导航失败时的代理轮换最大尝试次数（含首次）。小池建议 2，大池可到 3。
    max_proxy_attempts: 3,
    // 代理连通性与鉴权校验配置
    validation: {
      enabled: false, // 默认关闭自动预校验；改由前端按钮触发
      test_url: 'https://www.gov.hk/',
      timeout_seconds: 15,
      expect_status: 200,
    },
    // 临时黑名单与TTL（分钟）。按钮校验失败的代理写入此列表并在到期后自动恢复。
    temp_blacklist: [],
    blacklist_ttl_minutes: 60,
    // 本月被标记为流量已用尽的 provider（可手动清除）
    quota_exceeded: [],
  },

  // -----------------------------
  // 监控配置
  // -----------------------------
  monitoring: {},

  // -----------------------------
  // 网络配置
  // -----------------------------
  // HTTP请求相关的网络设置和重试策略
  // 包含目标服务器URL、请求头、超时和重试机制
  // -----------------------------
  network: {
    // 主要服务器URL（优先使用）
    base_url: 'https://abs1.td.gov.hk/tdab2/tdabs_external/AdminServlet_tchinese',

    // 备用服务器URL列表，支持故障转移和负载均衡
    base_urls: [
      'https://abs1.td.gov.hk/tdab2/tdabs_external/AdminServlet_tchinese', // 主服务器1
      'https://abs2.td.gov.hk/tdab2/tdabs_external/AdminServlet_tchinese', // 主服务器2
      'https://abs.td.gov.hk/tdab2/tdabs_external/AdminServlet_tchinese', // 备用服务器
    ],

    // 用户代理字符串：已废弃（由预约记录或运行期随机生成）
    // user_agent: 'deprecated',
    // 资源拦截：默认开启以提升性能并减少流量（图片/媒体/字体/常见分析脚本）
    block_resources: true,

    request_timeout_seconds: 20, // HTTP请求超时时间（秒）
    global_min_interval_seconds: 1.2, // 全局最小请求间隔（秒），防止请求过于频繁
    max_connections_per_host: 10, // 每个域名最大连接数（undici模式）
    use_undici: true, // 是否使用undici作为HTTP客户端（默认启用，性能更好）
  },

  // -----------------------------
  // 预约配置
  // -----------------------------
  // 自动预约系统的核心配置，包含业务逻辑、表单映射和流程控制
  // 针对香港运输署驾驶执照预约系统的专门配置
  // -----------------------------
  booking: {
    // 可用时间窗口天数
    available_window_days: 70,
    // 服务类型配置
    service_type: 'DI', // 默认服务类型：DI(免试签发)
    // 默认办公地点代码
    default_office_code: 'HKLO',
    // 人性化延迟配置
    // 模拟真人操作的时间延迟，避免被检测为机器人
    human_like_delays: {
      page_view_delay: { min: 2.0, max: 4.0 }, // 页面浏览延迟范围（秒）
      pre_submit_delay: { min: 2.0, max: 3.0 }, // 提交前延迟范围（秒）
    },
    // 多进程配置（默认关闭，逐步开启）
    multiprocess: {
      enabled: false, // 默认关闭，逐步开启
      maxWorkers: 50, // 默认50个Worker（与易语言工具一致）
      minWorkers: 3, // 最小Worker数（保证基本可用）
      workerTimeout: 60000, // Worker超时时间（60秒）
      healthCheckInterval: 5000, // 健康检查间隔（5秒）
      defaultMode: 'request', // 默认使用Request模式（轻量级）
      fallbackToPlaywright: false, // 不降级到Playwright
    },
  },

  // -----------------------------
  // 日志配置
  // -----------------------------
  // 完整的日志系统配置，包含级别控制、文件管理、采样和指标收集
  // 支持多种输出方式和高级功能（追踪ID、采样、指标统计）
  // -----------------------------
  logging: {
    // 基础日志配置
    log_level: 'INFO', // 日志级别：DEBUG | INFO | WARN | ERROR
    log_retention: '7 days', // 日志保留时间

    // 输出配置
    console_output: true, // 是否输出到控制台
    file_output: true, // 是否输出到文件

    // 调试/落盘配置（统一）
    save_html: true, // 是否保存 HTML（请求/自动化均生效）
    save_snapshots: true, // 是否保存快照/截图

    // 采样配置（防止日志洪水）
    sampling: {
      enabled: true, // 是否启用日志采样
      debug_sample_rate: 0.1, // DEBUG级别采样率（0-1）
      info_sample_rate: 1.0, // INFO级别采样率（0-1）
      warn_sample_rate: 1.0, // WARN级别采样率（0-1）
      // 注：ERROR级别不采样，确保所有错误都被记录
    },

    // 指标收集配置
    metrics: {
      enabled: true, // 是否启用日志指标收集
      reset_interval_hours: 24, // 指标重置间隔（小时）
    },

    // 自动化快照每日归档
    archive_snapshots_enabled: false,
    archive_snapshots_today: false,
    remove_original_snapshots_after_archive: false,
  },

  // -----------------------------
  // 提醒配置
  // -----------------------------
  notifications: {},
}

// ===============================
// 配置加载和管理函数
// ===============================

/**
 * 加载应用程序配置
 *
 * 配置加载优先级：
 * 1. 数据库配置（优先）
 * 2. 自定义配置文件 (userData/config/default.json)
 * 3. 环境变量覆盖
 * 4. 默认配置（兜底）
 *
 * @returns Promise<AppConfig> 完整的应用程序配置对象
 *
 * @example
 * ```typescript
 * // 在应用启动时加载配置
 * const config = await loadConfig()
 * console.log('日志级别:', config.logging.log_level)
 * ```
 */
export async function loadConfig(): Promise<AppConfig> {
  // 如果配置已加载，直接返回
  if (config)
    return config

  try {
    // 1. 准备默认配置（包含文件配置覆盖）
    let baseConfig = defaultConfig

    // 尝试加载自定义配置文件
    // 处理 Node.js 环境（测试时）和 Electron 环境
    let userDataPath: string
    try {
      userDataPath = app.getPath('userData')
    }
    catch {
      // Node.js 环境，使用临时目录或当前目录
      userDataPath = join(process.cwd(), '.test-data')
    }
    const configPath = join(userDataPath, 'config', 'default.json')
    try {
      const configFile = await readFile(configPath, 'utf-8')
      const customConfig = JSON.parse(configFile)
      baseConfig = mergeConfig(defaultConfig, customConfig)
      // 合并 providers：仅补入默认中启用的新增供应商（避免示例免费池干扰）
      try {
        const currentProviders = Array.isArray((baseConfig as any)?.proxy?.providers) ? ((baseConfig as any).proxy.providers as any[]) : []
        const idSet = new Set<string>(currentProviders.map((p: any) => String(p?.id || '')))
        const defaultEnabled = (providers as any[]).filter(p => p && p.enabled === true)
        const additions = defaultEnabled.filter((p: any) => !idSet.has(String(p.id || '')))
        if (additions.length > 0) {
          (baseConfig as any).proxy = (baseConfig as any).proxy || {}
          const merged = currentProviders.concat(additions.map(p => ({ ...p })))
          ;(baseConfig as any).proxy.providers = merged
          console.log(`ℹ️ 已补入默认启用的代理供应商: ${additions.map(a => a.id).join(', ')}`)
        }
      }
      catch {}
      console.log('✅ 加载自定义配置文件:', configPath)
    }
    catch {
      console.log('ℹ️ 未找到自定义配置文件，使用默认配置')
    }

    // 2. 暂时使用文件配置（数据库模式待 Prisma 客户端生成完成后启用）
    config = baseConfig
    console.log('✅ 配置加载完成（文件 + 默认）')

    return config as AppConfig
  }
  catch (error) {
    // 如果配置加载过程中出现任何错误，回退到默认配置
    console.error('❌ 配置加载失败:', error)
    config = defaultConfig
    return config as AppConfig
  }
}

/**
 * 获取当前应用程序配置
 *
 * 注意：必须先调用 loadConfig() 加载配置后才能使用此函数
 *
 * @returns AppConfig 当前的应用程序配置对象
 * @throws Error 如果配置尚未加载
 *
 * @example
 * ```typescript
 * // 获取当前配置
 * const config = getConfig()
 *
 * // 访问具体配置项
 * console.log('日志级别:', config.logging.log_level)
 * ```
 */
export function getConfig(): AppConfig {
  if (!config) {
    throw new Error('配置尚未加载，请先调用 loadConfig()')
  }
  return config as AppConfig
}

/**
 * 动态更新应用程序配置
 *
 * 支持部分配置更新，会与现有配置进行深度合并
 * 更新后的配置立即生效，无需重启应用
 * 同时保存到数据库以实现持久化
 *
 * @param updates Partial<AppConfig> 要更新的配置项（支持部分更新）
 * @returns AppConfig 更新后的完整配置对象
 * @throws Error 如果配置尚未加载
 *
 * @example
 * ```typescript
 * // 更新日志级别
 * const updatedConfig = await updateConfig({
 *   logging: {
 *     log_level: 'DEBUG',
 *     console_output: true
 *   }
 * })
 *
 * // 更新日志级别
 * await updateConfig({
 *   logging: {
 *     log_level: 'DEBUG'
 *   }
 * })
 * ```
 */
export async function updateConfig(updates: Partial<AppConfig>): Promise<AppConfig> {
  if (!config) {
    throw new Error('配置尚未加载，请先调用 loadConfig()')
  }

  // 更新内存中的配置
  config = mergeConfig(config, updates)

  // 保存到配置文件
  try {
    // 处理 Node.js 环境（测试时）和 Electron 环境
    let userDataPath: string
    try {
      userDataPath = app.getPath('userData')
    }
    catch {
      // Node.js 环境，使用临时目录
      userDataPath = join(process.cwd(), '.test-data')
    }
    const configPath = join(userDataPath, 'config', 'default.json')
    await mkdir(join(userDataPath, 'config'), { recursive: true })
    await writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8')
    console.log('✅ 配置已保存到文件:', configPath)
  }
  catch (error) {
    console.warn('⚠️ 配置保存到文件失败:', error)
  }

  return config as AppConfig
}

/**
 * 重置配置到默认值
 *
 * @returns AppConfig 重置后的配置
 */
export async function resetConfig(): Promise<AppConfig> {
  try {
    // 重置为 defaultConfig，并持久化到配置文件
    // 处理 Node.js 环境（测试时）和 Electron 环境
    let userDataPath: string
    try {
      userDataPath = app.getPath('userData')
    }
    catch {
      // Node.js 环境，使用临时目录
      userDataPath = join(process.cwd(), '.test-data')
    }
    const cfgDir = join(userDataPath, 'config')
    const configPath = join(cfgDir, 'default.json')
    await mkdir(cfgDir, { recursive: true })

    // 深拷贝默认配置，避免后续不小心修改常量对象
    const defaults = JSON.parse(JSON.stringify(defaultConfig)) as AppConfig

    // 写入默认配置到文件
    await writeFile(configPath, JSON.stringify(defaults, null, 2), 'utf-8')
    console.log('✅ 配置已重置为默认值并写入文件:', configPath)

    // 更新内存中的配置并返回
    config = defaults
    return config as AppConfig
  }
  catch (error) {
    console.error('❌ 重置配置失败:', error)
    throw error
  }
}

/**
 * 深度合并配置对象
 *
 * 递归合并两个配置对象，source 对象的值会覆盖 target 对象的对应值
 * 对于嵌套对象，会进行深度合并而不是直接替换
 *
 * @param target any 目标配置对象（基础配置）
 * @param source any 源配置对象（要合并的配置）
 * @returns any 合并后的配置对象
 *
 * @example
 * ```typescript
 * const base = {
 *   logging: { level: 'INFO', console: true },
 *   network: { timeout: 30 }
 * }
 * const updates = {
 *   logging: { level: 'DEBUG' },
 *   proxy: { enabled: true }
 * }
 *
 * const merged = mergeConfig(base, updates)
 * // 结果: {
 * //   logging: { level: 'DEBUG', console: true },
 * //   network: { timeout: 30 },
 * //   proxy: { enabled: true }
 * // }
 * ```
 */
function mergeConfig(target: any, source: any): any {
  const result = { ...target }

  for (const key in source) {
    if (source[key] !== null && typeof source[key] === 'object' && !Array.isArray(source[key])) {
      // 递归合并嵌套对象
      result[key] = mergeConfig(target[key] || {}, source[key])
    }
    else {
      // 直接覆盖原始值、数组和null值
      result[key] = source[key]
    }
  }

  return result
}

// ===============================
// 路径获取工具函数
// ===============================

/**
 * 获取应用程序数据目录
 *
 * 返回Electron应用的用户数据目录路径，用于存储应用相关的所有文件
 * 不同操作系统的路径：
 * - Windows: %APPDATA%/应用名
 * - macOS: ~/Library/Application Support/应用名
 * - Linux: ~/.config/应用名
 *
 * @returns string 应用数据目录的完整路径
 *
 * @example
 * ```typescript
 * const dataPath = getAppDataPath()
 * console.log('应用数据目录:', dataPath)
 * // 输出: C:\Users\用户名\AppData\Roaming\auto-tools
 * ```
 */
export function getAppDataPath(): string {
  try {
    return app.getPath('userData')
  }
  catch {
    // Node.js 环境，返回临时目录
    return join(process.cwd(), '.test-data')
  }
}

/**
 * 获取日志文件存储目录
 *
 * 返回日志文件的存储路径，所有日志文件都会保存在此目录下
 * 目录结构: {userData}/logs/
 *
 * @returns string 日志目录的完整路径
 *
 * @example
 * ```typescript
 * const logsPath = getLogsPath()
 * console.log('日志目录:', logsPath)
 * // 输出: C:\Users\用户名\AppData\Roaming\auto-tools\logs
 * ```
 */
export function getLogsPath(): string {
  // 开发环境：写入项目根目录 logs，便于查看与版本控制忽略
  // 生产环境（打包）：写入 userData/logs 确保可写
  const isPackaged = (() => {
    try {
      return app?.isPackaged || false
    }
    catch {
      return !!(process as any)?.resourcesPath
    }
  })()
  if (!isPackaged) {
    // 注意：主进程工作目录为 app/，Worker 进程可能在不同目录
    // 检查是否在 app/ 目录
    const cwd = process.cwd()
    if (cwd.endsWith('app')) {
      return join(cwd, '..', 'logs')
    }
    // 否则使用当前目录的 logs 子目录
    return join(cwd, 'logs')
  }
  return join(getAppDataPath(), 'logs')
}

/**
 * 获取应用数据文件存储目录
 *
 * 返回应用数据文件（如数据库、配置文件等）的存储路径
 * 目录结构: {userData}/data/
 *
 * @returns string 数据目录的完整路径
 *
 * @example
 * ```typescript
 * const dataPath = getDataPath()
 * console.log('数据目录:', dataPath)
 * // 输出: C:\Users\用户名\AppData\Roaming\auto-tools\data
 * ```
 */
export function getDataPath(): string {
  // 允许通过环境变量覆盖数据目录（脚本/工具场景）
  try {
    const override = (process as any)?.env?.AUTO_TOOLS_DATA_PATH
    if (override && typeof override === 'string' && override.trim().length > 0)
      return override
  }
  catch {}
  return join(getAppDataPath(), 'data')
}

/**
 * 获取临时HTML文件存储目录
 *
 * 返回临时HTML快照文件的存储路径，用于调试和问题排查
 * 当启用HTML响应保存功能时，抓取的网页内容会保存在此目录
 * 目录结构: {userData}/automation-run-snapshots/
 *
 * 可通过环境变量覆盖当前运行的快照子目录：
 * - AUTOMATION_SNAPSHOT_DIR 指向本次运行的目录（如按“时间戳_ID_姓名”）
 *
 * @returns string 临时HTML目录的完整路径
 *
 * @example
 * ```typescript
 * const tempHtmlPath = getTempHtmlPath()
 * console.log('临时HTML目录:', tempHtmlPath)
 * // 输出: C:\Users\用户名\AppData\Roaming\auto-tools\automation-run-snapshots
 * ```
 */
export function getTempHtmlPath(): string {
  // 若运行期指定了快照子目录（由自动化运行器设置），优先返回
  const envDir = process.env.AUTOMATION_SNAPSHOT_DIR
  if (envDir && typeof envDir === 'string' && envDir.length > 0)
    return envDir
  const isPackaged = (() => {
    try {
      return app?.isPackaged || false
    }
    catch {
      return !!(process as any)?.resourcesPath
    }
  })()
  if (!isPackaged) {
    // 统一开发期快照目录到 app/automation-run-snapshots
    const cwd = process.cwd()
    if (cwd.endsWith('app')) {
      return join(cwd, 'automation-run-snapshots')
    }
    return join(cwd, 'app', 'automation-run-snapshots')
  }
  // 生产环境放置在用户数据目录下 automation-run-snapshots
  return join(getAppDataPath(), 'automation-run-snapshots')
}

/**
 * 获取“查询/更改/取消预约（request）”的HTML快照目录
 * - 开发环境：app/request-run-snapshots
 * - 生产环境：{userData}/request-run-snapshots
 *
 * 可通过环境变量 REQUEST_SNAPSHOT_DIR 指定当前运行的子目录。
 */
export function getRequestHtmlPath(): string {
  const envDir = (process as any)?.env?.REQUEST_SNAPSHOT_DIR
  if (envDir && typeof envDir === 'string' && envDir.length > 0)
    return envDir
  const isPackaged = (() => {
    try {
      return app?.isPackaged || false
    }
    catch {
      return !!(process as any)?.resourcesPath
    }
  })()
  if (!isPackaged) {
    const cwd = process.cwd()
    if (cwd.endsWith('app')) {
      return join(cwd, 'request-run-snapshots')
    }
    return join(cwd, 'app', 'request-run-snapshots')
  }
  return join(getAppDataPath(), 'request-run-snapshots')
}

// ===============================
// 配置系统架构说明
// ===============================
//
// 本配置系统采用分层设计，支持多种配置来源和灵活的覆盖机制：
//
// 1. 📋 配置层级（优先级从高到低）：
//    - 环境变量（最高优先级）
//    - 自定义配置文件 (userData/config/default.json)
//    - 默认配置值（代码中定义）
//
// 2. 🔧 主要配置模块：
//    - proxy: 网络代理和IP轮换设置
//    - monitoring: 自动监控和预约策略
//    - network: HTTP请求和重试机制
//    - booking: 预约流程和业务逻辑
//    - logging: 日志系统和调试功能
//    - gui: 用户界面显示设置
//    - features: 功能开关和特性控制
//
// 3. 🚀 使用流程：
//    ```typescript
//    // 1. 应用启动时加载配置
//    await loadConfig()
//
//    // 2. 在任何地方获取配置
//    const config = getConfig()
//
//    // 3. 动态更新配置
//    updateConfig({ logging: { log_level: 'DEBUG' } })
//    ```
//
// 4. 📁 目录结构：
//    userData/
//    ├── config/           # 配置文件目录
//    │   └── default.json  # 自定义配置文件
//    ├── logs/            # 日志文件目录
//    ├── data/            # 数据库和数据文件
//    └── automation-run-snapshots/  # 临时HTML快照文件
//
// 5. 🌍 环境变量支持：
//    所有配置项都可以通过环境变量覆盖，命名规则：
//    SECTION_FIELD_NAME（如：LOG_LEVEL, NETWORK_TIMEOUT）
//
// ===============================
